package jwt

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
	"github.com/redis/go-redis/v9"
	"net/http"
	"strings"
	"time"
)

var AtKey = "moyn8y9abnd7q4zkq2m73yw8tu9j5ixm"

type RedisJWTHandler struct {
	cmd redis.Cmdable
}

func NewRedisJWTHandler(cmd redis.Cmdable) Handler {
	return &RedisJWTHandler{
		cmd: cmd,
	}
}

func (h *RedisJWTHandler) SetLoginToken(ctx *gin.Context, id int64) error {
	ssid := uuid.New().String()
	err := h.SetJWTToken(ctx, id, ssid)
	if err != nil {
		return err
	}

	err = h.SetRefreshToken(ctx, id, ssid)
	if err != nil {
		return err
	}

	return nil
}

func (h *RedisJWTHandler) SetJWTToken(ctx *gin.Context, id int64, ssid string) error {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaims{
		Id:   id,
		Ssid: ssid,
		RegisteredClaims: jwt.RegisteredClaims{
			//设置过期时间为 10分钟
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 10)),
		},
	})

	tokenStr, err := token.SignedString([]byte(AtKey))
	if err != nil {
		return err
	}
	ctx.Header("X-Jwt-Token", tokenStr)
	return nil
}

func (h *RedisJWTHandler) SetRefreshToken(ctx *gin.Context, id int64, ssid string) error {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, RefreshClaims{
		Id:   id,
		Ssid: ssid,
		RegisteredClaims: jwt.RegisteredClaims{
			//设置过期时间为 7天
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)),
		},
	})

	tokenStr, err := token.SignedString([]byte(AtKey))
	if err != nil {
		return err
	}
	ctx.Header("x-refresh-token", tokenStr)
	return nil
}

func (h *RedisJWTHandler) CleanToken(ctx *gin.Context) error {
	//这里的过期时间的设置，可以动态的计算当前token的剩余时间进行设置，而不是固定的设置长token的初始过期时间
	ctx.Header("X-Jwt-Token", "")
	ctx.Header("x-refresh-token", "")
	uc := ctx.MustGet("user").(UserClaims)

	//获取jwt的过期时间
	ttl := uc.ExpiresAt.Sub(time.Now())
	err := h.cmd.Set(ctx, fmt.Sprintf("users:ssid:%s", uc.Ssid), "", ttl).Err()
	return err
}

func (h *RedisJWTHandler) CheckSession(ctx *gin.Context, ssid string) (bool, error) {
	cnt, err := h.cmd.Exists(ctx, fmt.Sprintf("users:ssid:%s", ssid)).Result()
	if err != nil {
		//redis可能崩了
		return false, err
	}
	//key存在
	return cnt == 1, nil
}

// 这里直接返回空串，不返回false是因为后续我们会对token进行解析，会报错的
func (h *RedisJWTHandler) ExtractToken(ctx *gin.Context) string {
	headStr := ctx.GetHeader("Authorization")
	if headStr == "" {
		//未登录状态
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return ""
	}

	auth := strings.Split(headStr, " ")
	if len(auth) != 2 {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return ""
	}

	return auth[1]
}
